package com.robooot.business_counter.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.robooot.business_counter.exception.BizException;
import org.apache.commons.beanutils.PropertyUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class JsonUtil {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static <T> T object2Bean(Object object, Class<T> clazz) {
        return MAPPER.convertValue(object, clazz);
    }

    public static <T> String bean2JsonStr(T obj) {
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new BizException(8102, e.getMessage());
        }
    }

    public static <T> T jsonStr2Bean(String jsonStr, Class<T> clazz) {
        try {
            return MAPPER.readValue(jsonStr, clazz);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BizException(8102, e.getMessage());
        }
    }

    public static <T> List<T> str2List(String jsonData, Class<T> beanType) {
        JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
        try {
            List<T> list = MAPPER.readValue(jsonData, javaType);
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException(8102, e.getMessage());
        }
    }

    public static <T> String bean2JsonStr(T obj, String... props) {
        try {
            if (props == null || props.length == 0) {
                return bean2JsonStr(obj);
            }

            if (obj == null) {
                return null;
            }

            Map<String, Map> propMap = getPropMap(props);

            Map m = new HashMap();
            fillMap(m, obj, propMap);
            return MAPPER.writeValueAsString(m);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new BizException(8102, e.getMessage());
        }
    }

    private static Map<String, Map> getPropMap(String... props) {
        Map<String, Map> propMap = new HashMap<>();
        for (String key : props) {
            int splitIndex = key.indexOf('.');
            Map<String, Map> map = propMap;

            while (splitIndex != -1) {
                String prefix = key.substring(0, splitIndex);
                Map<String, Map> prop = (Map<String, Map>) map.get(prefix);
                if (prop == null) {
                    prop = new HashMap<>();
                    map.put(prefix, prop);
                }

                key = key.substring(splitIndex + 1);
                splitIndex = key.indexOf('.');
                map = prop;
            }

            map.put(key, Collections.EMPTY_MAP);
        }

        return propMap;
    }

    private static Object getProperty(Object obj, String propertyName) {
        try {
            return PropertyUtils.getProperty(obj, propertyName);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e.getTargetException());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void fillMap(Map m, Object obj, Map<String, Map> propMap) {
        for (Map.Entry<String, Map> e : propMap.entrySet()) {
            String prop = e.getKey();
            Object value = getProperty(obj, prop);
            if (value == null) {
                continue;
            }

            Map<String, Map> subprops = e.getValue();
            if (subprops.isEmpty()) {
                m.put(prop, value);
            } else {
                if (value instanceof Iterable) {
                    List list = new ArrayList();
                    for (Object o : (Iterable) value) {
                        Map mm = new HashMap();
                        fillMap(mm, o, subprops);
                        list.add(mm);
                    }

                    m.put(prop, list);
                } else {
                    Map mm = new HashMap();
                    m.put(prop, mm);
                    fillMap(mm, value, subprops);
                }
            }
        }
    }

    /**
     * 少于或等于4个字母：直接显示
     * 5到6个字母：中间的2个字母设置为星号
     * 7到8个字母：中间的3个字母设置为星号
     * 9到10个字母：中间的4个字母设置为星号
     * 11个或以上的字母：中间的5个字母设置为星号
     */
    public static String hiddenChar(String str) {
        if (str == null) {
            return "";
        }

        int length = str.length();
        if (length <= 4) {
            return str;
        }

        int mid = length / 2;
        if (length <= 6) {
            return str.substring(0, mid - 1) + "**" + str.substring(mid + 1);
        }

        if (length <= 8) {
            return str.substring(0, mid - 1) + "***" + str.substring(mid + 2);
        }

        if (length <= 10) {
            return str.substring(0, mid - 2) + "****" + str.substring(mid + 2);
        }

        return str.substring(0, mid - 2) + "*****" + str.substring(mid + 3);
    }
}
